package gwtappcontainer.server.apis.security;

import static gwtappcontainer.server.apis.security.OfyService.ofy;
import gwtappcontainer.server.DatastoreNamespaces;
import gwtappcontainer.server.apis.APIException;
import gwtappcontainer.shared.apis.APIResponse.Status;
import gwtappcontainer.shared.apis.security.UserProp;

import java.util.List;
import java.util.TreeSet;

import com.google.appengine.api.NamespaceManager;
import com.googlecode.objectify.Ref;


public class UserRepository {
			
	private final static String NAMESPACE = DatastoreNamespaces.SECURITY;
	
	public static TreeSet<UserProp> getAllUsers() {
		NamespaceManager.set(NAMESPACE);
		
		List<UserEntity> allEntities = ofy().load().type(UserEntity.class).list();
		TreeSet<UserProp> allProps = new TreeSet<UserProp>();
		
		for (UserEntity entity : allEntities) {
			allProps.add(entity.toProp());
		}		
		
		return allProps;
	}
	
	public static UserProp getUser(String email) {
		NamespaceManager.set(NAMESPACE);
		
		email = email.toLowerCase();
		UserEntity entity = ofy().load().type(UserEntity.class)
				.id(email).get();
		
		if (null == entity)
			return null;
		
		return entity.toProp();
	}
	
	public static void addUser(String email, String login) {
		
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		NamespaceManager.set(NAMESPACE);
		email = email.toLowerCase();
		
		UserEntity entity = ofy().load().type(UserEntity.class).id(email).get();
		
		if (entity != null)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"There is already an existing user with email [" + email + "]");
		
		entity = new UserEntity();
		entity.email = email;
		ofy().save().entity(entity).now();		
	}
	
	public static void deleteUser(String email, String login) {
		
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		
		NamespaceManager.set(NAMESPACE);
		email = email.toLowerCase();
		
		UserEntity entity = ofy().load().type(UserEntity.class).id(email).get();
		
		if (entity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"There is no existing user with email [" + email + "]");
		
		ofy().delete().entity(entity).now();	
	}
	
	public static void assignPrivilegeToUser(String email, String privilege, String login) {
		
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		
		NamespaceManager.set(NAMESPACE);
		email = email.toLowerCase();
		privilege = privilege.toUpperCase();
		UserEntity entity = ofy().load().type(UserEntity.class).id(email).get();
		
		if (entity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"User [" + email + "] not found");
		
		if (! PrivilegeRepository.getAllPrivileges().contains(privilege))
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"Privilege [" + privilege + "] is not valid");
		
		if (entity.privileges.contains(privilege))
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"User [" + email + "] already has privilege [" + privilege + "]");
		
		entity.privileges.add(privilege);
		ofy().save().entity(entity).now();
		
	}
	
	public static void unassignPrivilegeToUser(String email, String privilege, String login) {
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		
		NamespaceManager.set(NAMESPACE);
		email = email.toLowerCase();		
		UserEntity entity = ofy().load().type(UserEntity.class).id(email).get();
		
		if (entity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"User [" + email + "] not found");
		
		privilege = privilege.toUpperCase();
		if (! PrivilegeRepository.getAllPrivileges().contains(privilege))
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"Privilege [" + privilege + "] is not valid");
		
		if (! entity.privileges.contains(privilege))
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"User [" + email + "] already does not have privilege [" + privilege + "]");
		
		entity.privileges.remove(privilege);
		ofy().save().entity(entity).now();
	}
	
	public static void assignRoleToUser(String email, String role, String login) {
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		
		NamespaceManager.set(NAMESPACE);
		email = email.toLowerCase();		
		UserEntity userEntity = ofy().load().type(UserEntity.class).id(email).get();		
		if (userEntity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"User [" + email + "] not found");
		
		role = role.toUpperCase();				
		Ref<RoleEntity> roleEntity = ofy().load().type(RoleEntity.class).
				filter("role", role).first();		
		if (roleEntity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"Role [" + role + "] not found");
		
		userEntity.roles.add(roleEntity);
		ofy().save().entity(userEntity).now();
	}
	
	public static void unassignRoleToUser(String email, String role, String login) {
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		
		NamespaceManager.set(NAMESPACE);
		email = email.toLowerCase();		
		UserEntity userEntity = ofy().load().type(UserEntity.class).id(email).get();		
		if (userEntity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"User [" + email + "] not found");
		
		role = role.toUpperCase();				
		Ref<RoleEntity> roleEntity = ofy().load().type(RoleEntity.class).
				filter("role", role).first();		
		if (roleEntity == null)
			throw new APIException(Status.ERROR_RESOURCE_DOES_NOT_EXIST, 
					"Role [" + role + "] not found");
		
		userEntity.roles.remove(roleEntity);
		ofy().save().entity(userEntity).now();
	}
	
	public static void cloneUser(String newEmail, String existingEmail, String login) {
		
		GateKeeper.ensurePrivilege(login, Privileges.EDIT_USER);
		
		NamespaceManager.set(NAMESPACE);
		
		newEmail = newEmail.toLowerCase();
		existingEmail = existingEmail.toLowerCase();
		
		//newEmail should not be present and oldEmail should be already present
		UserEntity entity = ofy().load().type(UserEntity.class).id(newEmail).get();
		if (entity != null)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"User [" + newEmail + "] already present");
		
		entity = ofy().load().type(UserEntity.class).id(existingEmail).get();
		if (entity == null)
			throw new APIException(Status.ERROR_RESOURCE_ALREADY_EXISTS, 
					"User [" + existingEmail + "] is not present");
		
		entity.email = newEmail;
		ofy().save().entity(entity).now();
	}		
}
